package khleuven.mondzorg.domain;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.json.JSONArray;

import khleuven.mondzorg.db.DBException;
import khleuven.mondzorg.db.writers.OfflineSourceWriter;
import khleuven.mondzorg.db.writers.OfflineSourceWriterFactory;
import khleuven.mondzorg.db.writers.OfflineSourceWritingType;

public class DentalCareApp {
	
	private static volatile DentalCareApp uniqueAppInstance;
	
	private Language language = Language.DUTCH;
	private DestinationGroup destination = DestinationGroup.ADULTS;
	
	private Illustrations allIllustrations;	
	private Entries allEntries;	

	/**
	 * private constructor
	 */
	private DentalCareApp() {
		this.allEntries = new Entries();
		this.allIllustrations = new Illustrations();
	}
	
	/**
	 * creates the only instance of DentalCareApp
	 * @return the only instance of DentalCareApp
	 */
	public static DentalCareApp getInstance(){
		if(uniqueAppInstance == null){
			synchronized (DentalCareApp.class) {
				if(uniqueAppInstance == null){
					uniqueAppInstance = new DentalCareApp();
				}
			}
		}
		return uniqueAppInstance;
	}
	
	/**
	 * destroys the only instance of DentalCareApp
	 */
	public static void destroyInstance() {
		uniqueAppInstance=null;		
	}
	
	public Language getLanguage() {
		return language;
	}

	/**
	 * The language can be changed (it is a setting)
	 * @param language
	 */
	public void setLanguage(Language language) {
		this.language = language;
	}

	public DestinationGroup getDestination() {
		return destination;
	}

	/**
	 * The destination can be changed (it is a setting)
	 * @param destination
	 */
	public void setDestination(DestinationGroup destination) {		
		this.destination = destination;
	}
	
	/**
	 * Method to add an illustration to the app.
	 * Only illustrations that are added here can be used.
	 * @param illustration
	 */
	public void add(Illustration illustration) throws DomainException{
		if(illustration == null){
			throw new DomainException("Invalid illustration");
		}
		allIllustrations.add(illustration);
	}
		
	public void add(Entry entry) throws DomainException{
		if(entry == null){
			throw new DomainException("Invalid entry");
		}
		allEntries.add(entry);
	}
	
	public Illustration getIllustration(String illustration_id) throws DomainException{
		if(!allIllustrations.contains(illustration_id)){
			throw new DomainException("No illustration with this id + "+illustration_id);
		}
		return allIllustrations.get(illustration_id);
	}
	
	public ArrayList<Entry> getAllEntries(){
		return allEntries.getAllEntries();
	}
	
	public HashSet<Entry> getPreferredEntriesOfType(EntryType type){
		return allEntries.getPreferredEntries(type, getLanguage(), getDestination());
	}
	
	public ArrayList<String> getPreferredEntriesOfTypeAsStringList(EntryType type){
		return allEntries.getPreferredEntriesAsStringList(type, getLanguage(), getDestination());
	}
	
	public Illustrations getEntryIllustrations(EntryType type, String listedString){
		return allEntries.getEntryIllustrations(type, listedString, getLanguage(), getDestination());
	}
	
	public Illustrations getEntryIllustrations(Entry entry){
		return entry.getIllustrations();
	}
	
	public Entry getEntry(EntryType type, String listedString){
		return allEntries.getEntry(type, listedString, getLanguage(), getDestination());
	}
	
	public String replaceDefinitionsByLinks(String toReplaceallLinks, String toSkip) {
		ArrayList<String> alldefstrings = getPreferredEntriesOfTypeAsStringList(EntryType.DEFINITION);
		String explanationWithLinks = toReplaceallLinks;
		for(String defString: alldefstrings){
			if(!toSkip.equalsIgnoreCase(defString)){
				Pattern p = Pattern.compile(defString, Pattern.CASE_INSENSITIVE);
				Matcher m = p.matcher(explanationWithLinks);
				explanationWithLinks = m.replaceAll("<a href=\""+defString+"\">"+defString+"</a>");
			}
		}
		return explanationWithLinks;
	}
	
	public int getNbOfIllustrations() {
		return allIllustrations.size();
	}

	public LinkedHashMap<EntryType, Integer> getNbOfEntriesPerType() {
		return allEntries.getNbOfEntriesPerType();
	}
	
	//Dit heeft te maken met het offline wegschrijven	
	public void storeOffline(OfflineSourceWritingType type) throws DomainException{
		OfflineSourceWriter sourceWriter = 
				OfflineSourceWriterFactory.createOfflineSourceWriter(type);
		for(EntryType entrytype: EntryType.values()){
			JSONArray output = new JSONArray();
			if(entrytype == EntryType.ILLUSTRATION){				
				for(Illustration ill: allIllustrations.getIllustrations()){
					output.put(ill.toJSon());
				}
			} else{
				for(Entry entry: allEntries.getAllEntries(entrytype)){
					output.put(entry.toJSon());
				}
			}
			try {
				sourceWriter.write(entrytype.getFileOutputStream(),output);
			} catch (DBException e) {
				throw new DomainException("Cannot store memory to file", e);
			}
		}
	}
}
